﻿using mozhi.smarterp.Finance.Accounts;
using mozhi.smarterp.Finance.Enums;
using Mozhi.Abp.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;

namespace mozhi.smarterp.Finance.Finances.Services.EndPeriodProcess
{
    /// <summary>
    /// 结转销售成本
    /// </summary>
    public class TransferCostEndPeriodProcessor : EndPeriodProcessorBase<AccountBalance>
    {
        public TransferCostEndPeriodProcessor(EndPeriodProcessItem item, int period, IServiceProvider serviceProvider) : base(item, period, serviceProvider)
        {
        }

        protected override async Task LoadCalculationDataAsync()
        {
            await base.LoadCalculationDataAsync();
        }

        /// <summary>
        /// 获取处理结果
        ///1.收入为0，成本也为0，不需要结转
        ///2.如果收入不为0，则默认百分比 =  库存商品余额 / 收入
        ///3.如果上期有用户输入的百分比，则采用上期的，如果根据上百分比计算出来的成本金额大于库存商品余额，则使用第二条规则。
        ///4.结转百分比可以为任何值，如果结转百分比超过100%，点击创建的时候，弹出确认框给用户确认
        /// </summary>
        /// <returns></returns>
        protected override async Task<EndPeriodProcessResult> HandleDataAsync()
        {
            EndPeriodProcessResult result = null;

            //取综合本位币金额
            _data = _data.Where(x => x.CurrencyId == null && x.DimensionGroupValueId == null).ToList();

            ///主营业务收入科目
            var account6001 = GetAccount(GetEndPeriodProcessSettingAsync("6001")?.CustomAccountCode);

            if (account6001 == null)
                return result;

            //库存商品科目
            var account1405 = GetAccount(GetEndPeriodProcessSettingAsync("1405")?.CustomAccountCode);

            if (account1405 == null)
            {
                return result;
            }

            //主营业收入余额
            var balances6001 = _data.Where(x => x.AccountId == account6001.Id).ToList();

            //库存商品余额
            var balances1405 = _data.Where(x => x.AccountId == account1405.Id).ToList();

            var incomeAmount = balances6001.Sum(x => x.CreditAmount) - balances6001.Sum(x => x.DebitAmount);

            //结转金额
            var transferAmount = 0.00m;
            //两个为0，不需要处理
            if (incomeAmount == null || incomeAmount == 0)
            {
                result = new EndPeriodProcessResult(_guidGenerator.Create(), _item.Id, _period, EndPeriodProcessType.TransferCost);

                result.Change(false, false, null, 0, false);

                var emptyItems = GetResultItems(result.Id, 0, 0, 0);

                foreach (var item in emptyItems)
                {
                    result.AddItem(item);
                }
                return result;
            }
 
            //结转比例
            var carryForwardPercent = await GetCarryForwardPercnet(incomeAmount.Value, balances1405.Sum(x => x.ClosingAmount));

            transferAmount = (incomeAmount.Value * carryForwardPercent / 100M);

            var inventoryAmount = balances1405.Where(x => x.ClosingAmount.HasValue).Sum(x => x.ClosingAmount.Value);

            result = new EndPeriodProcessResult(_guidGenerator.Create(), _item.Id, _period, EndPeriodProcessType.TransferCost);

            result.Change(true, false, null, transferAmount.ToDecimal(2), true);

            var items = GetResultItems(result.Id, incomeAmount.HasValue ? incomeAmount.Value : 0, carryForwardPercent, inventoryAmount);

            foreach (var item in items)
            {
                result.AddItem(item);
            }

            return result;
        }


        /// <summary>
        /// 处理凭证创建逻辑
        /// </summary>
        /// <returns></returns>
        protected override async Task<Journal> HandleGenerateJournalAsync()
        {
            _data = _data.Where(x => x.DimensionGroupValueId != null && x.CurrencyId != null).ToList();

            ///主营业务收入科目
            var account6001 = GetFirstLeafAccount(GetEndPeriodProcessSettingAsync("6001")?.CustomAccountCode);

            //主营业务成本科目
            var account6401 = GetFirstLeafAccount(GetEndPeriodProcessSettingAsync("6401")?.CustomAccountCode);

            //库存商品科目
            var account1405 = GetFirstLeafAccount(GetEndPeriodProcessSettingAsync("1405")?.CustomAccountCode);

            //结转比例是否存在
            var resultPercentItem = _processResult.Items?.FirstOrDefault(x => x.Index == 2);

            if (resultPercentItem == null || resultPercentItem.Value.IsNullOrEmpty())
            {
                var tips = "无法获取该期末处理项的百分比";
                throw new UserFriendlyException(tips, "100000");
            }

            var carryForwardPercnet = decimal.Parse(resultPercentItem.Value).ToDecimal(6);

            //主营业收入余额
            var balances6001 = _data.Where(x => x.AccountId == account6001.Id).ToList();

            var journalMananger = _serviceProvider.GetService(typeof(JournalManager)) as JournalManager;

            //按照核算维度进行结转

            var defaultJournalNumberPrefix = await GetJournalDefaultNumberPrefix();

            var journal = await journalMananger.Create(_guidGenerator.Create(), defaultJournalNumberPrefix.Id, _period.ToLastDayDateTime());

            var journalItemFor6001 = await GetJournalItemsAsync(balances6001, account6401, carryForwardPercnet, AccountDirectionType.Debit);

            var journalItemFor1405 = await GetJournalItemsAsync(balances6001, account1405, carryForwardPercnet, AccountDirectionType.Credit);

            var journalItems = journalItemFor6001;
            journalItems.AddRange(journalItemFor1405);

            await journalMananger.ChangeItems(journal, journalItems);

            return journal;
        }


        #region
        /// <summary>
        /// 获取结转比例
        /// </summary>
        /// <param name="incomeAmount">主营业务收入金额</param>
        /// <param name="incomeAmount">库存商品金额</param>
        /// <returns></returns>
        private async Task<decimal> GetCarryForwardPercnet(decimal incomeAmount, decimal? inventoryAmount)
        {
            decimal result = 0.00M;

            var previousPeriod = _period.ToPrePeriod();
            //查上一期的处理结果
            var previousProcessResult = await GetEndPeriodResultAsync(previousPeriod);

            var carryForwardPercnetProcessResultItem = previousProcessResult?.Items?.FirstOrDefault(x => x.Index == 2);

            var previousRate = carryForwardPercnetProcessResultItem == null ? -1 : decimal.Parse(carryForwardPercnetProcessResultItem.Value);

            //如果上期有用户输入的百分比，则采用上期的，如果根据上百分比计算出来的成本金额大于库存商品余额，则使用第二条规则。
            if (previousRate>0 && ((previousRate * incomeAmount) / 100M).ToMoney() <= inventoryAmount)
                return previousRate;

            result = inventoryAmount.HasValue ? (inventoryAmount.Value / incomeAmount) * 100 : 0;

            result = result <= 0 ? 0 : result.ToDecimal(6);

            //不能超过100
            result = result > 100 ? 100 : result;

            return result;

        }


        private List<EndPeriodProcessResultItem> GetResultItems(Guid resultId , decimal incomeAmount, decimal carryForwardPercnet, decimal finishedGoodsAmount)
        {
            var result = new List<EndPeriodProcessResultItem>();

            //设置子处理项
            result.Add(new EndPeriodProcessResultItem(_guidGenerator.Create() , resultId , "主营业务收入" ,incomeAmount.ToString() , 1 , 1 ,false));

            result.Add(new EndPeriodProcessResultItem(_guidGenerator.Create(), resultId, "结转比例", carryForwardPercnet.ToDecimal(6).ToString(), 2, 2, true));

            result.Add(new EndPeriodProcessResultItem(_guidGenerator.Create(), resultId, "库存商品余额", finishedGoodsAmount.ToDecimal(2).ToString(), 1, 3, false));

            return result;
        }


        /// <summary>
        /// 获取凭证分录
        /// </summary>
        /// <param name="balances"></param>
        /// <param name="account"></param>
        /// <returns></returns>
        private async Task<List<JournalItem>> GetJournalItemsAsync(List<AccountBalance> balances, Account account, decimal carryForwardPercnet, AccountDirectionType direction)
        {
            var result = new List<JournalItem>();

            if (balances == null || balances.Count == 0 || account == null)
                return result;

            string summary = "结转销售成本";

            if (account.DimensionItems == null || account.DimensionItems.Count == 0)
            {
                //如果科目没有启用核算维度，就直接生成一个不带核算维度的凭证的分录

                //收入金额
                var incomeAmount = balances.Sum(x => x.CreditAmount) - balances.Sum(x => x.DebitAmount);

                var transferAmount = ((incomeAmount * carryForwardPercnet) / 100M).ToMoney();

                var journalItem = await GetJournalItemAsync(summary, transferAmount, account, null, 0, direction);

                result.Add(journalItem);

                return result;
            }
            else
            {
                //带核算维度，按照核算维度进行结转
                var dimensions = account.DimensionItems;

                var groupBalances = balances.GroupBy(x => x.DimensionGroupValueId);

                var loopIndex = 0;

                //处理过了的余额，没处理的需要放入未指定中
                var groupBalancesIds = new List<Guid>();

                foreach (IGrouping<string, AccountBalance> info in groupBalances)
                {
                    var key = info.Key;

                    var tempBalances = info.ToList();

                    if (tempBalances == null || tempBalances.Count == 0)
                        continue;

                    groupBalancesIds.AddRange(tempBalances.Select(x => x.Id).ToList());

                    var incomeAmount = tempBalances.Sum(x => x.CreditAmount) - tempBalances.Sum(x => x.DebitAmount);

                    var transferAmount = ((incomeAmount * carryForwardPercnet) / 100M).ToDecimal(2);

                    var journalItem = await GetJournalItemAsync(summary ,transferAmount, account, tempBalances.First().DimensionGroupValueId , loopIndex, direction);

                    result.Add(journalItem);

                    loopIndex++;
                }

                var unprocessBalances = balances.Where(x => !groupBalancesIds.Contains(x.Id)).ToList();

                if (unprocessBalances.Count > 0)
                {
                    var incomeAmount = unprocessBalances.Sum(x => x.CreditAmount) - unprocessBalances.Sum(x => x.DebitAmount);

                    var transferAmount = ((incomeAmount * carryForwardPercnet) / 100M).ToDecimal(2);

                    var journalItem = await GetJournalItemAsync(summary, transferAmount, account, null, loopIndex + 1, direction);

                    result.Add(journalItem);
                }

                return result;
            }
        }


        #endregion
    }
}
